#include "log.h"
#include <qdebug.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define MVIRT "VmSize"
#define MRES "VmRSS"
#define MDATA "VmData"
#define MSTACK "VmStk"

typedef struct {
    float VIRT;
    float RES;
    float DATA;
    float STACK;
} MemInfo;

float getSzInMB(char *szStr) {

    while( (*szStr) && !isdigit(*szStr) ) {
        ++szStr;
    }

    int i=0;
    while(isdigit(szStr[i])) ++i;
    szStr[i] = 0;

    float result = atoi(szStr);
    result /= 1024.0f;

    return result;
}

#define BUFSZ 1024
void getMemInfo(MemInfo *out) {
    char buf[BUFSZ];

    out->VIRT = out->RES = out->DATA = out->STACK = -1;

    FILE *f = fopen("/proc/self/status","r");
    if(f) {
        while(!feof(f)) {
            fgets(buf,BUFSZ,f);
            if(strstr(buf,MVIRT)) {
                out->VIRT = getSzInMB(buf+strlen(MVIRT));
            } else if(strstr(buf,MRES)) {
                out->RES = getSzInMB(buf+strlen(MRES));
            } else if(strstr(buf,MDATA)) {
                out->DATA = getSzInMB(buf+strlen(MDATA));
            } else if(strstr(buf,MSTACK)) {
                out->STACK = getSzInMB(buf+strlen(MSTACK));
            }
        }
        fclose(f);
    }
}

QString buildLogMessage(const char *prefix, const char *fname, const char *funcName, unsigned int line, const char *msg) {
    MemInfo minfo;

    getMemInfo(&minfo);

    QString logMsg(prefix);
    logMsg.append(" [");
    logMsg.append(fname);
    logMsg.append(", ");
    logMsg.append(QString::number(line));
    logMsg.append("] ");
    logMsg.append(funcName);
    logMsg.append("(): ");
    logMsg.append(msg);

    logMsg.append("\tMEM -> VIRT: ");
    logMsg.append(QString::number(minfo.VIRT));
    logMsg.append(" | RES: ");
    logMsg.append(QString::number(minfo.RES));
    logMsg.append(" | DATA: ");
    logMsg.append(QString::number(minfo.DATA));
    logMsg.append(" | STACK: ");
    logMsg.append(QString::number(minfo.STACK));

    return logMsg;
}

FILE *logFile(bool reopen) {
    static FILE *f = 0;
    static int d=0,m=0,a=0;

    time_t timep = time(0);
    struct tm *time = localtime(&timep);
    if( reopen || (!f) || (d!=time->tm_mday) || (m!=time->tm_mon) || (a!=time->tm_year) ) {
        d = time->tm_mday;
        m = time->tm_mon;
        a = time->tm_year;
        if(f) fclose(f);

        char fname[1024];
        sprintf(fname,"/var/log/coletor_%d_%d_%d.txt",d,m+1,a+1900);
        f = fopen(fname,"a");
    }

    return f;
}

void logMessage(QString logMsg) {
    FILE *f = logFile(false);
    int retries = 5;

    do {
        time_t timep = time(0);
        struct tm *time = localtime(&timep);

        bool error = true;
        if(f) {
            struct stat st;
            fstat(fileno(f),&st);

            if(st.st_nlink) { //verifica se o arquivo foi deletado por outro processo.
                int res = fprintf(f,"%2d:%2d:%2d %s\n",time->tm_hour,time->tm_min,time->tm_sec,logMsg.toStdString().c_str());
                if(res>=0) {
                    int flushErr = fflush(f);
                    if(!flushErr) error = false;
                }
            }
        }

        if(!error) break;
        f = logFile(true);
    } while(retries--);

//    qDebug(logMsg.toUtf8());
}

int logLevel = 0;
void setLogLevel(int level) {
    logLevel = level;
}

void logCritical(const char *fname, const char *funcName, unsigned int line, const char *msg) {
    if(logLevel>=CRITICAL_L) {
        logMessage(buildLogMessage("[CRITICAL]",fname,funcName,line,msg));
    }
}

void logError(const char *fname, const char *funcName, unsigned int line, const char *msg) {
    if(logLevel>=ERROR_L) {
        logMessage(buildLogMessage("[ERROR]",fname,funcName,line,msg));
    }
}

void logWarning(const char *fname, const char *funcName, unsigned int line, const char *msg) {
    if(logLevel>=WARNING_L) {
        logMessage(buildLogMessage("[WARNING]",fname,funcName,line,msg));
    }
}

void logInfo(const char *fname, const char *funcName, unsigned int line, const char *msg) {
    if(logLevel>=INFO_L) {
        logMessage(buildLogMessage("[INFO]",fname,funcName,line,msg));
    }
}

void logDebug(const char *fname, const char *funcName, unsigned int line, const char *msg) {
    if(logLevel>=DEBUG_L) {
        logMessage(buildLogMessage("[DEBUG]",fname,funcName,line,msg));
    }
}

